﻿using Antlr4.Runtime.Misc;
using System;
using System.Collections.Generic;
using System.Text;

namespace TT.Compiler
{
    public class CompilerContext
    {
        ILogger logger;
        public Dictionary<string, Library> libs = new Dictionary<string, Library>();

        public TT.Runtime.TTEnv result = new Runtime.TTEnv();
        public bool DebugMode = false;
        public CompilerContext(ILogger logger, bool DebugMode = false)
        {
            this.logger = logger;
            this.DebugMode = DebugMode;
        }
        public Library CreateLib_Simple(string file)
        {
            Library lib = new Library(this, logger, DebugMode);
            var b = lib.ParseCodeDefine("_noname", file);
            if (!b)
                return null;
            //var b = lib.Compile();

            libs.Add(lib.name, lib);
            return lib;
        }
        public Library CreateLib(string configfilename)
        {
            Library lib = new Library(this, logger, DebugMode);
            var b = lib.Build(configfilename);
            if (!b)
                return null;
            libs.Add(lib.name, lib);
            return lib;
        }

    }




    public class Library
    {
        CompilerContext context;
        ILogger logger;
        bool DebugMode;
        public Library(CompilerContext context, ILogger logger, bool DebugMode = false)
        {
            this.name = "noname";
            this.context = context;
            this.logger = logger;
            this.DebugMode = DebugMode;

            var glabolClass = Define.TypeDefine.CreateGlabol();
            this.typeDef[glabolClass.FullName] = glabolClass;
        }
        public Config config;
        public string name;
        public bool hadLinked;
        //命名空间直接是名字的一部分
        //类种类视为命名空间
        //默认类@glabol
        //所以定义的第一层只能是Class
        public Dictionary<string, Define.TypeDefine> typeDef = new Dictionary<string, Define.TypeDefine>();
        public bool Build(string configfilename)
        {
            if (System.IO.File.Exists(configfilename) == false) return false;
            config = Config.FromFile(configfilename);
            name = config.GetProjectName();

            //Load Depends
            //var depsucc = true;
            //foreach (var dep in config.GetDepends())
            //{
            //    if (context.libs.ContainsKey(dep.name) == false)
            //    {
            //        if (dep.type == Config.DependType.Library)
            //        {
            //            var b = context.LoadLib(dep.path);
            //            if (null == b)
            //                depsucc = false;
            //        }
            //        else if (dep.type == Config.DependType.Project)
            //        {
            //            var b = context.CreateLib(dep.path);
            //            if (null == b)
            //                depsucc = false;
            //        }
            //    }
            //}
            //if (!depsucc)
            //    return false;
            //Parse Code Define
            var folder = config.GetCodePath();
            var codes = System.IO.Directory.GetFiles(folder, "*.kfc", System.IO.SearchOption.AllDirectories);
            bool bsucc = true;
            foreach (var file in codes)
            {
                var code = System.IO.File.ReadAllText(file);
                var b = ParseCodeDefine(file, code);
                if (!b)
                    bsucc = false;
            }
            //DoCompiler

            //Link
            if (!bsucc)
                return false;
            return Link();

        }


        static Antlr4.Runtime.ParserRuleContext ParseAst(string text)
        {
            Antlr4.Runtime.AntlrInputStream input = new Antlr4.Runtime.AntlrInputStream(text);
            var lex = new ttparser.ttlangLexer(input);
            var tokenstream = new Antlr4.Runtime.CommonTokenStream(lex);
            var parser = new ttparser.ttlangParser(tokenstream);

            var exprroot = parser.root();
            return exprroot;
        }
        //拆开源码，把各个部分收集整理到一起
        public bool ParseCodeDefine(string name, string code)
        {
            var ast = ParseAst(code);
            if (ast.exception != null)
            {
                if (DebugMode)
                {
                    var erpos = ast.exception.OffendingToken;
                    Console.WriteLine("ParseCode (" + name + ") Error at:(" + erpos.Line + "," + erpos.Column + ")");
                    Console.WriteLine("    " + ast.exception.Message);
                }
                return false;
            }
            //编译应该是两遍，第一遍得出所有的定义（名字，内部，外部，引用），第二遍开始编译
            //因为没有足够的定义信息，在编译成员定义时，就会有无法消除的歧义
            var defineVistor = new Define.DefineVistor();
            var doc = defineVistor.Visit(ast) as Define.TypeDefine;



            foreach (var i in doc.Fields)
            {
                this.typeDef[Define.TypeDefine.glabolDefineName].AddField(i);
            }
            foreach (var i in doc.Funcs)
            {
                this.typeDef[Define.TypeDefine.glabolDefineName].AddFunc(i);
            }
            foreach (var i in doc.GetAllOtherDefines())
            {
                if (this.typeDef.ContainsKey(i.FullName))
                    throw new Exception("already have a class named:" + i.FullName);
                this.typeDef[i.FullName] = i;
            }
            //c
            return true;
        }

        bool Link()
        {
            return false;
        }
    }


}
